﻿using System;
using System.Text;

namespace SkeinLib
{
    public static partial class SkeinCore
    {
        //CONSTANTS

        // flags and version
        private const UInt32 SKEIN_VERSION = 0x00000001;     // base major version ONE (1)
        private const UInt32 SKEIN_SCHEMA_ID = 0x33414853;   // (0x33414853) = "SHA3" in ASCII per the white paper
        private const UInt64 SKEIN_VS = (UInt64)(((UInt64)SKEIN_VERSION << 32) + SKEIN_SCHEMA_ID);

        private const UInt64 SKEIN_TREELEVEL_MULTIPLIER = 0x1000000000000;
        private const UInt64 SKEIN_FLAG_FIRST = 0x4000000000000000;     //=First block in a series of data blocks.
        private const UInt64 SKEIN_FLAG_FINAL = 0x8000000000000000;     //=Last block in a series of data blocks. Not mutually exclusive with First.
        private const UInt64 SKEIN_FLAG_PAD = 0x0080000000000000;       //=The last block in a series of data blocks has an incomplete byte, top loaded.

        // block types being passed in
        private const UInt64 SKEIN_TYPE_KEY = 0x0000000000000000;       // =Skein being used for MAC, this is the Key block. (optional but done FIRST)
        private const UInt64 SKEIN_TYPE_CONFIG = 0x0400000000000000;    // =Standard config. (ALWAYS DONE, this can be precomputed and stored/reused:
                                                                        // IF AND ONLY IF there is no key (or the key is constant) AND the hashbitlength
                                                                        //   doesn't change.
                                                                        // i.e. if Key is always "blah" AND hashBitLength is always 160, precompute,
                                                                        // or there is no Key and hashBitLength is always 256, precompute, etc.
        private const UInt64 SKEIN_TYPE_PER_STR = 0x0800000000000000;   // =(optional) Personalization string. 
                                                                        // Use the Personalization string like a message block
                                                                        // after the config to narrow the scope of the particular
                                                                        // hash calculation.
        private const UInt64 SKEIN_TYPE_PUB_KEY = 0x0C00000000000000;   // =(optional) Processing a public key here will tie any hash operations to that
                                                                        // public key.  This is usefull for message signatures, encrypted or otherwise.
        private const UInt64 SKEIN_TYPE_KEY_DER_ID = 0x1000000000000000;// =(optional) Key derivation identifier.
                                                                        // Pass the master key in the Key stage, then pass the 
                                                                        // ID (or username) of the requested derived key in this stage.
        private const UInt64 SKEIN_TYPE_NONCE = 0x1400000000000000;     // =(optional) Message nonce.
                                                                        // Use for RNG or Stream Cipher modes, or key derivations.
                                                                        // Generally Nonces are used only ONCE in a given context, and then disposed.
        private const UInt64 SKEIN_TYPE_MSG = 0x3000000000000000;       // This block is message data.
        private const UInt64 SKEIN_TYPE_OUTPUT = 0x3F00000000000000;    // This block is part of the output transform.
        
        //CLASS-WIDE COMMON METHODS/FUNCTIONS

        /// <summary>
        /// Get the default, non-keyed initialized state array for a given word-width.
        /// 4 = 256 bit operation, 8 = 512 bit operation, 16 = 1024 bit operation
        /// </summary>
        /// <param name="wordWidth">The word-width of the required state.</param>
        /// <returns>An array of unsigned, 64-bit integers that represent the state.</returns>
        public static UInt64[] GetInitialUnkeyedState(byte wordWidth)
        {
            switch (wordWidth)
            {
                case 4:
                    return new UInt64[] { 0x164290a9d4eeef1dUL, 
                                          0x8e7eaf44b1b0cd15UL, 
                                          0xa8ba0822f69d09aeUL,
                                          0x0af25c5e364a6468UL };
                case 8:
                    return new UInt64[] { 0x1a9a721c8a265ca5UL, 0xc9abacf5aa853978UL, 
                                          0x4af6652ab80a2883UL, 0x66f5e8a809a773c7UL,
                                          0x7fa984b781baaf5bUL, 0x0fe5d2d93233f397UL, 
                                          0x6e29f932dcb412d7UL, 0xd40cd9472f225c23UL };
                case 16:
                    return new UInt64[] { 0x495e85b953876965UL, 0x1e3d5c1b41e754efUL, 0x237254552e9c10c7UL, 0x0b00aab4fa441407UL,
                                          0x17dda56aa106337cUL, 0xf98200e9cae13f94UL, 0xf2df7f00adff12bfUL, 0xa92673d0d0ca7ad9UL,
                                          0xc0dd64b04b27ed98UL, 0x87c36a6ca0a26f90UL, 0x640c8526d0850a10UL, 0x6ebfad0c93da09aeUL,
                                          0x617e3bcddee4a85fUL, 0x05a4a1a7d82737b7UL, 0x002baf2c3eb13d30UL, 0x28527a78c83d554cUL };
                default:
                    throw new ArgumentOutOfRangeException("wordWidth", "GetInitialUnkeyedState must be 4, 8 or 16 (for 256 bit, 512 bit, and 1024 bit respectively).");
            }
        }

        /// <summary>
        /// Zero out an array of 64-bit unsigned words in a secure manner.
        /// This function prevents optimization from removing the zeroizing of memory arrays.
        /// This has been tested in x86 and x64, the JIT compiler will remove .Initialize calls
        /// in the interest of performance, this function prevents that.
        /// </summary>
        /// <param name="inWords">Array to be zeroed out.</param>
        /// <param name="nullify">Set the array to NULL when complete.  Default = FALSE</param>
        public static void ClearWords(ref UInt64[] inWords, bool nullify = false)
        {
            if (inWords == null)
                return;
            for (int i = inWords.GetLowerBound(0); i <= inWords.GetUpperBound(0); i++)
            {
                // CRUCIAL!!!!
                // this extra stuff is to prevent the compiler from optimizing these steps out (tested and confirmed!)
                // this also helps to secure memory when disposing arrays.
                
                //old way
                //inWords[i] = 0;
                //inWords[i] |= 0xFFFFFFFFFFFFFFFF;
                //inWords[i] ^= inWords[i];

                //new way
                inWords[i] = 0xFFFFFFFFFFFFFFFF;
                inWords[i] ^= inWords[i];
            }
            if (nullify)
                inWords = null;
        }

        /// <summary>
        /// Zero out an array of bytes in a secure manner.
        /// This function prevents optimization from removing the zeroizing of memory arrays.
        /// This has been tested in x86 and x64, the JIT compiler will remove .Initialize calls
        /// in the interest of performance, this function prevents that.
        /// </summary>
        /// <param name="inBytes">Array to be zeroed out.</param>
        /// <param name="nullify">Set the array to NULL when complete.  Default = FALSE</param>
        public static void ClearBytes(ref byte[] inBytes, bool nullify = false)
        {
            if (inBytes == null)
                return;
            for (int i = inBytes.GetLowerBound(0); i <= inBytes.GetUpperBound(0); i++)
            {
                // CRUCIAL!!!!
                // this extra stuff is to prevent the compiler from optimizing these steps out (tested and confirmed!)
                // this also helps to secure memory when disposing arrays.
                
                //old way
                //inBytes[i] = 0;
                //inBytes[i] |= 0xFF;
                //inBytes[i] ^= inBytes[i];

                // new way
                inBytes[i] = 0xFF;
                inBytes[i] ^= inBytes[i];
            }
            if (nullify)
                inBytes = null;
        }

        /// <summary>
        /// Check the state of a given instance to be sure that it has been initialized to the correct size.
        /// </summary>
        /// <param name="state">The array to be checked.</param>
        /// <returns>If the state is not configured correctly, an exception explaining the problem, else null.</returns>
        internal static Exception ValidateState(UInt64[] state)
        {
            if (state != null && (state.Length == 4 || state.Length == 8 || state.Length == 16))
                return null;
            else
                return new ArgumentException("ValidateState Check: state must be initialized to an array of 4, 8, or 16 elements", "state");
        }

        /// <summary>
        /// Process a Skein UBI block.  This function is common to a number of UBI blocks that have a similar flow.
        /// Key, Personalization String, Public Key, Key Derivation ID, Nonce, and Message type blocks (not Config, or Output blocks). 
        /// </summary>
        /// <param name="state">The state of the current Skein instance.</param>
        /// <param name="data">The data to be processed.</param>
        /// <param name="typeFlag">What type of block is going to be processed.</param>
        /// <param name="lastByteBitCount">If the last byte of the block is incomplete/partial, 
        /// the number of actual bits packed into the high end of the byte.</param>
        private static void ProcessGeneric(ref UInt64[] state, UInt64 typeFlag, byte[] data = null, byte lastByteBitCount = 0)
        {   // used for key, message, personalization, nonce

            
            UInt64[] transport = new UInt64[state.Length];
            UInt64[] tweak = { 0, SKEIN_FLAG_FIRST + typeFlag };
            int byteWidth = Buffer.ByteLength(state);

            if (data == null)
                data = new byte[0];

            for (int i = 0; (i * byteWidth) < data.Length || (i == 0 && data.Length == 0 && typeFlag != SKEIN_TYPE_KEY); i++)
            {
                //process a block  
                int currentPosition = i * byteWidth;
                tweak[0] = (UInt64)currentPosition;
                if (data != null && currentPosition + byteWidth >= data.Length)
                {
                    ClearWords(ref transport);
                    //the tweak0 represents a RUNNING total of bytes, so if this is the last block, take the full length
                    //NOTE: as in the reference implementation, this implementation only supports 64 bits of length information,
                    // not the full 96 that are normally available as per the white paper.
                    tweak[0] = (ulong)data.Length; 

                    tweak[1] += SKEIN_FLAG_FINAL;
                    if (lastByteBitCount > 0)
                    {
                        tweak[1] += SKEIN_FLAG_PAD;
                        Buffer.BlockCopy(data, currentPosition, transport, 0, (data.Length - currentPosition) - 1);
                        byte newLastByte = (byte)data[data.Length - 1]; // grab the original last byte
                        newLastByte &= (byte)(0xFF << (8 - lastByteBitCount));
                        newLastByte += (byte)(Math.Pow(2, (double)(7 - lastByteBitCount))); //append a single bit
                        int newLastBytePosition = (data.Length - 1 + Buffer.ByteLength(transport)) % Buffer.ByteLength(transport);
                        //put the augmented last byte back into the transport
                        Buffer.SetByte(transport, newLastBytePosition, newLastByte);
                    }
                    else
                        Buffer.BlockCopy(data, currentPosition, transport, 0, data.Length - currentPosition);
                }
                else if(data != null)
                {
                    tweak[0] += (UInt64)byteWidth;
                    Buffer.BlockCopy(data, currentPosition, transport, 0, byteWidth);
                }
                //now transform
                switch (state.Length)
                {
                    case 4: ThreeFish.ThreeFish256Internal(transport, state, tweak, true, ref state); break;
                    case 8: ThreeFish.ThreeFish512Internal(transport, state, tweak, true, ref state); break;
                    case 16: ThreeFish.ThreeFish1024Internal(transport, state, tweak, true, ref state); break;
                }
                tweak[1] &= ~SKEIN_FLAG_FIRST; // we're not on the first block anymore, so knock off that flag
            }
            ClearWords(ref transport, true);
            ClearWords(ref tweak, true);
        }

        /// <summary>
        /// Initialize a Skein instance with a given state width and ultimate hashBitLength.
        /// The Hash Bit Length is configured into an instance at the very beginning, it must be known on initialization.
        /// </summary>
        /// <param name="stateWordWidth">4, 8, or 16 words, for 256-, 512- and 1024-bit state widths, respectively.</param>
        /// <param name="hashBitLength">The length, in bits, of the final output hash.</param>
        /// <param name="keyData">If the Skein transform is to be keyed, this key is supplied here, else null.</param>
        /// <returns>The initialized state of the transform.</returns>
        internal static UInt64[] Initialize(byte stateWordWidth, ulong hashBitLength, byte[] keyData = null)
        {
            //MAC OPERATION, USING A KEY
            if (stateWordWidth != 4 && stateWordWidth != 8 && stateWordWidth != 16)
                throw new ArgumentException("Initialize:  stateWordWidth must be length 4 (256bit), 8 (512bit), or 16 (1024bit)", "stateWordWidth");
            if (hashBitLength == 0)
                throw new ArgumentOutOfRangeException("hashBitLenth", "Initialize:  hashBitLength cannot be Zero.");
            if (hashBitLength % 8 != 0)
                throw new ArgumentException("Initialize:  hashBitLength must be byte aligned (evenly divisible by 8)", "hashBitLength");
            //if (keyData == null || keyData.Length < 1) // MUST BE CHECKED BY CALLER!!!
            //    throw new ArgumentException("Initialize:  keyData cannot be null or empty if specified", "keyData");

            UInt64[] state = new UInt64[stateWordWidth];
            if (keyData != null)
                ProcessGeneric(ref state, SKEIN_TYPE_KEY, keyData);
            ProcessConfig(ref state, hashBitLength, 0, 0, 0);

            return state;
        }

        /// <summary>
        /// Initialize a Skein instance with a given state width and ultimate hashBitLength.
        /// The Hash Bit Length is configured into an instance at the very beginning, it must be known on initialization.
        /// </summary>
        /// <param name="stateWordWidth">4, 8, or 16 words, for 256-, 512- and 1024-bit state widths, respectively.</param>
        /// <param name="hashBitLength">The length, in bits, of the final output hash.</param>
        /// <param name="keyData">If the Skein transform is to be keyed, this key is supplied here, else null.</param>
        /// <param name="treeLeaf"></param>
        /// <param name="treeNode"></param>
        /// <param name="treeMaxLevels"></param>
        /// <returns>The initialized state of the transform.</returns>
        internal static UInt64[] Initialize(byte stateWordWidth, ulong hashBitLength, byte[] keyData = null, byte treeLeaf = 0, byte treeNode = 0, byte treeMaxLevels = 0)
        {
            //MAC OPERATION, USING A KEY
            if (stateWordWidth != 4 && stateWordWidth != 8 && stateWordWidth != 16)
                throw new ArgumentException("Initialize:  stateWordWidth must be length 4 (256bit), 8 (512bit), or 16 (1024bit)", "stateWordWidth");
            if (hashBitLength == 0)
                throw new ArgumentOutOfRangeException("hashBitLenth", "Initialize:  hashBitLength cannot be Zero.");
            if (hashBitLength % 8 != 0)
                throw new ArgumentException("Initialize:  hashBitLength must be byte aligned (evenly divisible by 8)", "hashBitLength");
            //if (keyData == null || keyData.Length < 1) // MUST BE CHECKED BY CALLER!!!
            //    throw new ArgumentException("Initialize:  keyData cannot be null or empty if specified", "keyData");

            UInt64[] state = new UInt64[stateWordWidth];
            if (keyData != null)
                ProcessGeneric(ref state, SKEIN_TYPE_KEY, keyData);
            ProcessConfig(ref state, hashBitLength, treeLeaf, treeNode, treeMaxLevels);

            return state;
        }

        /// <summary>
        /// INTERNAL FUNCTION - THIS IS USED ONLY BY THE INITIALIZE FUNCTIONS, NOT BY THE END CODER
        /// Process the Configure step of a Skein transform (after an optional key has already been processed).
        /// NOTE:  If treeLeaf, treeNode, or treeMaxLevels is non-zero, they ALL must be non-zero, treeMaxLevels must be greater than 1.
        /// NOTE:  Tree hashing MUST NOT be used for RNG's.
        /// </summary>
        /// <param name="state">The state of the transform to be configured.</param>
        /// <param name="hashBitLength">The output hash bit-length of the transform.  This value must be known at the beginning.</param>
        /// <param name="treeLeaf">[OPTIONAL] The size of the leaf in a tree hashing operation.  Zero if not used.</param>
        /// <param name="treeNode">[OPTIONAL] The size of a node in a tree hashing operation.  Zero if not used.</param>
        /// <param name="treeMaxLevels">[OPTIONAL] The maximum number of levels in a tree hashing operation.  Zero if not used.</param>
        private static void ProcessConfig(ref UInt64[] state, ulong hashBitLength, byte treeLeaf = 0, byte treeNode = 0, byte treeMaxLevels = 0)
        {
            Exception ex = ValidateState(state);
            if (ex != null) 
                throw new ArgumentException("ProcessConfig:  state failed sanity check (see inner exception)", ex);
            if (hashBitLength == 0)
                throw new ArgumentOutOfRangeException("hashBitLength", "ProcessConfig:  hashBitLength cannot be zero");
            if ((treeLeaf == 0 || treeNode == 0 || treeMaxLevels == 0) && (treeLeaf + treeNode + treeMaxLevels > 0))
                throw new ArgumentException("ProcessConfig:  when using tree hashing, Leaf, Node, and MaxLevels cannot be zero");
            if (treeMaxLevels < 2 && treeLeaf + treeNode > 0)
                throw new ArgumentOutOfRangeException("treeMaxLevels", "ProcessConfig:  when tree hashing, treeMaxLevels must be greater than 1");

            UInt64[] transport = new UInt64[state.Length];
            UInt64[] tweak = { 32, SKEIN_FLAG_FIRST + SKEIN_FLAG_FINAL + SKEIN_TYPE_CONFIG };

            transport[0] = SKEIN_VS;
            transport[1] = hashBitLength;
            transport[2] = (ulong)((treeMaxLevels << 16) + (treeNode << 8) + treeLeaf);

            //SkeinDebug.WriteData2File(ref transport, "debug", "configTRANSPORT", "This is the transport input of the config.");
            //SkeinDebug.WriteData2File(ref tweak, "debug", "configTWEAK", "This is the tweak input of the config.");

            switch (state.Length)
            {
                case 4: ThreeFish.ThreeFish256Internal(transport, state, tweak, true, ref state); break;
                case 8: ThreeFish.ThreeFish512Internal(transport, state, tweak, true, ref state); break;
                case 16: ThreeFish.ThreeFish1024Internal(transport, state, tweak, true, ref state); break;
            }
            ClearWords(ref transport, true);
            //SkeinDebug.WriteData2File(ref state, "debug", "configAFTER", "This is the result of the config.");
        }

        /// <summary>
        /// Process Personalization step of an initialized Skein transform.
        /// </summary>
        /// <param name="state">The state of the transform to be personalized.</param>
        /// <param name="data">The personalization data to be injected into the transform.</param>
        internal static void ProcessPersonalization(ref UInt64[] state, byte[] data)
        {
            Exception ex = ValidateState(state);
            if (ex != null) 
                throw new ArgumentException("ProcessPersonalization:  state failed sanity check (see inner exception)", ex);
            if (data == null || data.Length < 1)
                throw new ArgumentException("ProcessPersonalization:  data cannot be null or empty", "data");

            ProcessGeneric(ref state, SKEIN_TYPE_PER_STR, data);
        }

        /// <summary>
        /// Process Personalization step of an initialized Skein transform.
        /// </summary>
        /// <param name="state">The state of the transform to be personalized.</param>
        /// <param name="personalization">The personalization string (which will be UTF-8 processed) to be injected into the transform.</param>
        internal static void ProcessPersonalization(ref UInt64[] state, ref string personalization)
        {
            Exception ex = ValidateState(state);
            if (ex != null) 
                throw new ArgumentException("ProcessPersonalization:  state failed sanity check (see inner exception)", ex);
            if (string.IsNullOrWhiteSpace(personalization))
                throw new ArgumentException("ProcessPersonalization:  personalization cannot be null or empty", "personalization");
            System.Text.UTF8Encoding myEncoder = new System.Text.UTF8Encoding();
            byte[] _data = myEncoder.GetBytes(personalization);
            
            ProcessGeneric(ref state, SKEIN_TYPE_PER_STR, _data);
        }

        /// <summary>
        /// Process a message or some data and tie it to a public key.
        /// </summary>
        /// <param name="state">The state of the transform which will be tied to the given public key.</param>
        /// <param name="data">The public key data as an array of bytes.</param>
        internal static void ProcessPublicKey(ref UInt64[] state, byte[] data)
        {
            Exception ex = ValidateState(state);
            if (ex != null) 
                throw new ArgumentException("ProcessPublicKey:  state failed sanity check (see inner exception)", ex);
            if (data == null || data.Length < 1)
                throw new ArgumentException("ProcessPublicKey:  data cannot be null or empty", "data");

            ProcessGeneric(ref state, SKEIN_TYPE_PUB_KEY, data);
        }

        /// <summary>
        /// Process a key or message and derive a new key from the master key injected into the transform.
        /// The master key must have been passed as the key during Initialize step.
        /// </summary>
        /// <param name="state">The state of the transform that will process key data.</param>
        /// <param name="data">The user ID to be injected into the transform as an array of bytes.</param>
        internal static void ProcessKeyId(ref UInt64[] state, byte[] data)
        {
            Exception ex = ValidateState(state);
            if (ex != null) 
                throw new ArgumentException("ProcessKeyId:  state failed sanity check (see inner exception)", ex);
            if (data == null || data.Length < 1)
                throw new ArgumentException("ProcessKeyId:  data cannot be null or empty", "data");

            ProcessGeneric(ref state, SKEIN_TYPE_KEY_DER_ID, data);
        }

        /// <summary>
        /// Process a nonce that will "tweak" the transform for a particular message or circumstance.
        /// </summary>
        /// <param name="state">The state of the transform to be tweaked.</param>
        /// <param name="data">The data to be injected into the transform for tweaking it as an array of bytes.</param>
        internal static void ProcessNonce(ref UInt64[] state, byte[] data)
        {
            Exception ex = ValidateState(state);
            if (ex != null) 
                throw new ArgumentException("ProcessNonce:  state failed sanity check (see inner exception)", ex);
            if (data == null || data.Length < 1)
                throw new ArgumentException("ProcessNonce:  data cannot be null or empty", "data");

            ProcessGeneric(ref state, SKEIN_TYPE_NONCE, data);
        }

        /// <summary>
        /// Process a message that will be transformed.
        /// </summary>
        /// <param name="state">The state that is ready to process a message.</param>
        /// <param name="data">The data of the message in a byte array.</param>
        /// <param name="lastByteBitCount">[OPTIONAL] If the last byte of data has only a few bits packed into the high end, pass the number of bits to process, else 0.</param>
        internal static void ProcessMessage(ref UInt64[] state, byte[] data, byte lastByteBitCount = 0)
        {
            Exception ex = ValidateState(state);
            if (ex != null) 
                throw new ArgumentException("ProcessMessage:  state failed sanity check (see inner exception)", ex);
            if (lastByteBitCount > 7)
                throw new ArgumentOutOfRangeException("lastByteBitCount", "ProcessMessage:  the last byte count cannot exceed 7 bits. " +
                    "If the last byte is complete (no padding), pass zero.");
            //if (data == null || data.Length < 1) // THIS MUST BE CHECKED BY THE CALLER!!!
            //    throw new ArgumentException("ProcessMessage:  data cannot be null or empty", "data");

            ProcessGeneric(ref state, SKEIN_TYPE_MSG, data, lastByteBitCount);
        }

        /// <summary>
        /// Process a message that will be transformed.
        /// </summary>
        /// <param name="state">The state that is ready to process a message.</param>
        /// <param name="message">The message to be transformed (it will be UTF-8 processed regardless).</param>
        internal static void ProcessMessage(ref UInt64[] state, ref string message)
        {
            Exception ex = ValidateState(state);
            if (ex != null) 
                throw new ArgumentException("ProcessMessage:  state failed sanity check (see inner exception)", ex);

            System.Text.UTF8Encoding myEncoder = new System.Text.UTF8Encoding();
            byte[] _data = myEncoder.GetBytes(message);
            myEncoder = null;
            ProcessGeneric(ref state, SKEIN_TYPE_MSG, _data);
            ClearBytes(ref _data, true);
            System.GC.Collect();
        }

        /// <summary>
        /// Finalize the transform and retrieve the hash as an array of bytes.  This will destroy the state upon completion.
        /// This is not to be used for RNG or PRNG operations.
        /// </summary>
        /// <param name="state">The state from which to extract the hash.</param>
        /// <param name="hashBitLength">The length in bits of the final hash.  THIS MUST MATCH THE VALUE PASSED TO THE INITIALIZE STEP! </param>
        /// <returns>The resulting hash as an array of bytes.</returns>
        internal static byte[] ProcessStandardOutput(ref UInt64[] state, ulong hashBitLength)
        {
            Exception ex = ValidateState(state);
            if (ex != null) 
                throw new ArgumentException("ProcessStandardOutput:  state failed sanity check (see inner exception)", ex);
            if (hashBitLength < 1)
                throw new ArgumentException("ProcessStandardOutput:  hashBitLength cannot be negative or zero", "hashBitLength");
            if (hashBitLength % 8 != 0)
                throw new ArgumentException("ProcessStandardOutput:  hashBitLength must be byte aligned (evenly divisible by 8)", "hashBitLength");

            // Why the 8?  I have no idea, but I'm assuming that it means 8 bytes (64 bits)
            // It is not explained in the white paper
            UInt64[] tweak = { 8, SKEIN_FLAG_FIRST + SKEIN_TYPE_OUTPUT + SKEIN_FLAG_FINAL };
            UInt64[] transport = new UInt64[state.Length];

            //SkeinDebug.WriteData2File(ref transport, "debug", "stdoutputTRANSPORT", "Transport for the output.");
            //SkeinDebug.WriteData2File(ref tweak, "debug", "stdoutputTWEAK", "Tweak for the output.");
            //SkeinDebug.WriteData2File(ref state, "debug", "stdoutputSTATE", "State for the output.");

            //BEGIN OUTPUT
            int byteCount = (int)((hashBitLength + 7) >> 3);
            short byteWidth = (short)Buffer.ByteLength(state);
            byte[] result = new byte[hashBitLength / 8];
            for (int i = 0; (i * byteWidth) < result.Length; i++)
            {   //use the input array for a counter, only the state and tweak are used and remain constant
                ClearWords(ref transport);
                transport[0] = (UInt64)i;
                switch (state.Length)
                {
                    case 4: ThreeFish.ThreeFish256Internal(transport, state, tweak, true, ref transport); break;
                    case 8: ThreeFish.ThreeFish512Internal(transport, state, tweak, true, ref transport); break;
                    case 16: ThreeFish.ThreeFish1024Internal(transport, state, tweak, true, ref transport); break;
                }
                //dump the transport to the output, then discard
                int nextPosition = (i + 1) * byteWidth;
                int currentPosition = i * byteWidth;
                if (nextPosition <= result.Length)
                    Buffer.BlockCopy(transport, 0, result, currentPosition, (int)byteWidth);
                else
                    Buffer.BlockCopy(transport, 0, result, currentPosition, (int)(result.Length - currentPosition));
            }
            //zero out and drop used memory
            ClearWords(ref transport, true);
            ClearWords(ref state, true);
            ClearWords(ref tweak, true);
            //END OUTPUT
            return result;
        }

        #region Key Derivation Functions
        /// <summary>
        /// Derive a key from a master and an identifier.  Good for creating IV's or derivative keys.
        /// Calling this method does not affect any initialized instances.
        /// </summary>
        /// <param name="masterKey">The master key from which a new key will be derived.</param>
        /// <param name="identifier">The unique identifier for the new key, which is processed as part of the hash.</param>
        /// <param name="hashBitLength">The length, in bits (byte aligned) for the final output.  Default = 128</param>
        /// <param name="salt">Any additional salt material, as an array of bytes, to hash along with the master key and identifier (recommended).</param>
        /// <returns>The derived key of the specified bit length as an array of bytes.</returns>
        public static byte[] DeriveKey(byte wordWidth, byte[] masterKey, ref string identifier, int hashBitLength = 128, byte[] salt = null)
        {
            if (string.IsNullOrWhiteSpace(identifier))
                throw new ArgumentException("identifier cannot be null or empty", "identifier");

            UTF8Encoding myEncoder = new UTF8Encoding();
            return DeriveKey(wordWidth, masterKey, myEncoder.GetBytes(identifier), hashBitLength);
        }

        /// <summary>
        /// Derive a key from a master and an identifier.  Good for creating IV's or derivitive keys.
        /// Calling this method does not affect any initialized instances.
        /// </summary>
        /// <param name="wordWidth">The width of the state to use internally.</param>
        /// <param name="masterKey">The master key from which a new key will be derived.</param>
        /// <param name="identifier">The unique identifier for the new key, which is processed as part of the hash, as an array of bytes.</param>
        /// <param name="hashBitLength">The length, in bits (byte aligned) for the final output.  Default = 128</param>
        /// <param name="salt">Any additional salt material, as an array of bytes, to hash along with the master key and identifier (recommended).</param>
        /// <returns>The derived key of the specified bit length as an array of bytes.</returns>
        public static byte[] DeriveKey(byte wordWidth, byte[] masterKey, byte[] identifier, int hashBitLength = 128, byte[] salt = null)
        {
            if (identifier == null || identifier.Length < 1)
                throw new ArgumentException("identifier cannot be null or empty", "identifier");
            if (wordWidth != 4 && wordWidth != 8 && wordWidth != 16)
                throw new ArgumentOutOfRangeException("wordWidth", "Word width must be 4, 8, or 16.");

            UInt64[] tempState = SkeinCore.Initialize(wordWidth, (uint)hashBitLength, masterKey); //
            SkeinCore.ProcessKeyId(ref tempState, identifier);
            SkeinCore.ProcessMessage(ref tempState, salt);
            byte[] result = SkeinCore.ProcessStandardOutput(ref tempState, (uint)hashBitLength);
            SkeinCore.ClearWords(ref tempState, true);
            return result;
        }

        /// <summary>
        /// Provides for a PKCS #5 (RFC2898:PBKDF1) 'style' password derivation function using Skein.
        /// </summary>
        /// <param name="wordWidth">The width of the state to use internally.</param>
        /// <param name="password">The password string from which a key will be derived. (UTF-8 processed internally)</param>
        /// <param name="salt">The salt to add to the password during transformation.  NOT OPTIONAL, Range 8 to 32 bytes</param>
        /// <param name="iterationCount">[OPTIONAL] The number of times to expand out the password and the salt.  Default = 80, Range 10 to 1000</param>
        /// <param name="hashBitLength">[OPTIONAL] The length in bits of the final key.  Default = 128, cannot be 0</param>
        /// <returns>The derived key of the specified bit length as an array of bytes.</returns>
        public static byte[] DeriveKeyFromPassword(byte wordWidth, ref string password, byte[] salt, short iterationCount = 80, int hashBitLength = 128)
        {
            if (string.IsNullOrWhiteSpace(password))
                throw new ArgumentException("password cannot be null or empty", "password");
            if (wordWidth != 4 && wordWidth != 8 && wordWidth != 16)
                throw new ArgumentOutOfRangeException("wordWidth", "Word width must be 4, 8, or 16.");

            System.Text.UTF8Encoding myEncoder = new System.Text.UTF8Encoding();
            return DeriveKeyFromPassword(wordWidth, myEncoder.GetBytes(password), salt, iterationCount, hashBitLength);
        }

        /// <summary>
        /// Provides for a PKCS #5 (RFC2898:PBKDF1) 'style' password derivation function using Skein.
        /// </summary>
        /// <param name="wordWidth">The width of the state to use internally.</param>
        /// <param name="password">The password as an array of bytes from which a key will be derived.</param>
        /// <param name="salt">The salt to add to the password during transformation.  NOT OPTIONAL, Range 8 to 32 bytes</param>
        /// <param name="iterationCount">[OPTIONAL] The number of times to expand out the password and the salt.  Default = 80, Range 10 to 1000</param>
        /// <param name="hashBitLength">[OPTIONAL] The length in bits of the final key.  Default = 128, cannot be 0</param>
        /// <returns>The derived key of the specified bit length as an array of bytes.</returns>
        public static byte[] DeriveKeyFromPassword(byte wordWidth, byte[] password, byte[] salt, short iterationCount = 80, int hashBitLength = 128)
        {
            if (wordWidth != 4 && wordWidth != 8 && wordWidth != 16)
                throw new ArgumentOutOfRangeException("wordWidth", "Word width must be 4, 8, or 16.");
            if (hashBitLength == 0)
                throw new ArgumentOutOfRangeException("hashBitLength", "hashBitLength cannot be 0");
            if (password == null || password.Length < 1)
                throw new ArgumentException("password cannot be null or empty", "password");
            if (iterationCount < 10 || iterationCount > 1000)
                throw new ArgumentOutOfRangeException("iterationCount",
                    "iterationCount must be between 10 and 1000 for safety and effeciency");
            if (salt == null || salt.Length < 8)
                throw new ArgumentException("salt cannot be null and must be at least 64 bits (8 UTF-8 bytes)");
            if (salt.Length > 32)
                throw new ArgumentOutOfRangeException("salt cannot be longer than 256 bits (32 UTF-8 bytes)");

            UInt64[] tempState = SkeinCore.Initialize(wordWidth, (uint)hashBitLength, password);
            byte[] tempSalt = new byte[(password.Length + salt.Length) * iterationCount];
            for (int i = 0; i < iterationCount; i++)
            {
                int currentLocation = (password.Length + salt.Length) * i;
                Buffer.BlockCopy(password, 0, tempSalt, currentLocation, password.Length);
                Buffer.BlockCopy(salt, 0, tempSalt, currentLocation + password.Length, salt.Length);
            }
            SkeinCore.ProcessMessage(ref tempState, tempSalt);
            SkeinCore.ClearBytes(ref tempSalt, true);
            byte[] result = SkeinCore.ProcessStandardOutput(ref tempState, (uint)hashBitLength);
            SkeinCore.ClearWords(ref tempState, true);
            return result;
        }
        #endregion Key Derivation Functions
    }//end class
}//end namespace
